using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using NuGet.Configuration;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using NuGetBatchTool.Models;
using NuGet.Common;
using Microsoft.Extensions.Configuration;
using System.Net.Http;
using System.Threading;

namespace NuGetBatchTool.Services
{
    public class NuGetService
    {
        private readonly Dictionary<string, NuGetSource> _sources;
        private readonly string _configPath;
        private readonly ILogger _logger;

        public NuGetService(string configPath)
        {
            _configPath = configPath;
            _sources = LoadSources();
            _logger = new FileLogger("nugettool.log");
        }

        public IEnumerable<NuGetSource> GetSources()
        {
            return _sources.Values;
        }

        private Dictionary<string, NuGetSource> LoadSources()
        {
            var sources = new Dictionary<string, NuGetSource>();
            if (!File.Exists(_configPath))
                return sources;

            var config = new ConfigurationBuilder()
                .AddIniFile(_configPath, optional: false, reloadOnChange: false)
                .Build();

            foreach (var section in config.GetChildren())
            {
                var name = section.Key;
                var url = section["url"] ?? "";
                var apikey = section["apikey"] ?? "";
                var isDefault = bool.TryParse(section["isdefault"], out var b) ? b : false;

                if (string.IsNullOrWhiteSpace(url))
                    throw new Exception($"源 [{name}] 缺少 url 字段！");
                if (string.IsNullOrWhiteSpace(apikey))
                    throw new Exception($"源 [{name}] 缺少 apikey 字段！");

                sources[name] = new NuGetSource
                {
                    Name = name,
                    Url = url,
                    ApiKey = apikey,
                    IsDefault = isDefault
                };
            }

            return sources;
        }

        private ISettings GetSettings()
        {
            return Settings.LoadDefaultSettings(null);
        }

        private SourceRepository GetSourceRepository(string sourceName)
        {
            if (!_sources.TryGetValue(sourceName, out var source))
            {
                throw new ArgumentException($"Source '{sourceName}' not found in configuration.");
            }

            var packageSource = new PackageSource(source.Url, source.Name);
            return new SourceRepository(packageSource, Repository.Provider.GetCoreV3());
        }

        public async Task<List<IPackageSearchMetadata>> ListPackagesAsync(string sourceName, string? packageFilter = null)
        {
            var repository = GetSourceRepository(sourceName);
            var searchResource = await repository.GetResourceAsync<PackageSearchResource>();
            if (searchResource == null)
                throw new Exception($"源 [{sourceName}] 不支持包搜索，可能不是标准 NuGet V3 源。");

            // 禁用缓存，始终查最新
            var cacheContext = new SourceCacheContext { NoCache = true, DirectDownload = true, RefreshMemoryCache = true };
            var packages = await searchResource.SearchAsync(
                packageFilter ?? string.Empty,
                new SearchFilter(false),
                0,
                100,
                _logger,
                CancellationToken.None);

            return packages.ToList();
        }

        public async Task HidePackageAsync(string sourceName, string packageId, string version)
        {
            if (!_sources.TryGetValue(sourceName, out var source))
            {
                throw new ArgumentException($"Source '{sourceName}' not found in configuration.");
            }

            var repository = GetSourceRepository(sourceName);
            var packageUpdateResource = await repository.GetResourceAsync<PackageUpdateResource>();
            
            await packageUpdateResource.Delete(
                packageId,
                version,
                _ => source.ApiKey,
                _ => true,
                false,
                _logger);
        }

        public async Task UnhidePackageAsync(string sourceName, string packageId, string version)
        {
            // 只支持 nuget.org
            if (!_sources.TryGetValue(sourceName, out var source))
                throw new ArgumentException($"Source '{sourceName}' not found in configuration.");
            if (!source.Url.Contains("nuget.org"))
                throw new NotSupportedException("当前只支持 nuget.org relist");
            var apiKey = source.ApiKey;
            await RelistAsync(packageId, version, apiKey);
        }

        public static async Task RelistAsync(string id, string version, string key)
        {
            const string ApiKeyHeader = "X-NuGet-ApiKey";
            using var http = new HttpClient();
            http.DefaultRequestHeaders.Add(ApiKeyHeader, key);
            var ret = await http.PostAsync($"https://www.nuget.org/api/v2/package/{id.ToLowerInvariant()}/{version}", null).ConfigureAwait(false);
            if (!ret.IsSuccessStatusCode)
                throw new Exception($"显示失败：{ret.ReasonPhrase}");
        }

        public async Task DeletePackageAsync(string sourceName, string packageId, string version)
        {
            var repository = GetSourceRepository(sourceName);
            var packageUpdateResource = await repository.GetResourceAsync<PackageUpdateResource>();
            //packageUpdateResource.
            await packageUpdateResource.Delete(
                packageId,
                version,
                _ => _sources[sourceName].ApiKey,
                _ => true,
                false,
                _logger);
        }

        public async Task PushPackageAsync(string sourceName, string packageFile)
        {
            if (!_sources.TryGetValue(sourceName, out var source))
            {
                throw new ArgumentException($"Source '{sourceName}' not found in configuration.");
            }

            var repository = GetSourceRepository(sourceName);
            var packageUpdateResource = await repository.GetResourceAsync<PackageUpdateResource>();

            await packageUpdateResource.Push(
                new[] { packageFile },
                null,
                300,
                false,
                _ => source.ApiKey,
                _ => null,
                false,
                false,
                null,
                _logger);
        }

        public async Task<List<IPackageSearchMetadata>> ListAllVersionsAsync(string sourceName, string packageId)
        {
            var repository = GetSourceRepository(sourceName);
            var metadataResource = await repository.GetResourceAsync<PackageMetadataResource>();
            // 禁用缓存，始终查最新
            var cacheContext = new SourceCacheContext { NoCache = true, DirectDownload = true, RefreshMemoryCache = true };
            var versions = await metadataResource.GetMetadataAsync(
                packageId,
                includePrerelease: true,
                includeUnlisted: true,
                cacheContext,
                _logger,
                CancellationToken.None);

            return versions.ToList();
        }
    }

    public class FileLogger : ILogger
    {
        private readonly string _logFilePath;
        private static readonly object _logLock = new object();
        public FileLogger(string logFilePath)
        {
            _logFilePath = logFilePath;
        }

        private void WriteLog(string message)
        {
            lock (_logLock)
            {
                File.AppendAllText(_logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {message}\n");
            }
        }

        public void Log(LogLevel level, string data)
        {
            WriteLog($"[{level}] {data}");
        }
        public void Log(ILogMessage message)
        {
            WriteLog($"[{message.Level}] {message.Message}");
        }
        public Task LogAsync(LogLevel level, string data)
        {
            Log(level, data);
            return Task.CompletedTask;
        }
        public Task LogAsync(ILogMessage message)
        {
            Log(message);
            return Task.CompletedTask;
        }
        public void LogDebug(string data) => Log(LogLevel.Debug, data);
        public void LogError(string data) => Log(LogLevel.Error, data);
        public void LogInformation(string data) => Log(LogLevel.Information, data);
        public void LogInformationSummary(string data) => Log(LogLevel.Information, data);
        public void LogMinimal(string data) => Log(LogLevel.Minimal, data);
        public void LogVerbose(string data) => Log(LogLevel.Verbose, data);
        public void LogWarning(string data) => Log(LogLevel.Warning, data);
    }
}